import { EventEmitter } from "@feng3d/event";
import { Node, NodeEventMap } from "../../core/Node";
import { Matrix4 } from "../../thirdparty/three/exports";
import { Group, Mesh, Vector3 } from "../../thirdparty/three/imports";
import { Mesh3D } from "../objects/Mesh3D";

export interface Node3DEventMap extends NodeEventMap
{
}

export interface Node3D
{
    /**
     * 父对象
     */
    get parent(): Node3D;

    /**
     * 子对象列表
     */
    get children(): Node3D[];
    set children(v: Node3D[]);

    /**
     * 获取指定位置的子对象
     *
     * @param index 子对象位置。
     */
    getChildAt(index: number): Node3D;

    /**
     * 根据名称查找对象
     *
     * @param name 对象名称
     */
    find(name: string): Node3D;

    /**
     * 添加子对象
     *
     * @param child 子对象
     *
     * @returns 返回自身。
     */
    addChild(child: Node3D): this;
}

/**
 * 3D节点，包装`three`中`Group`作为3D节点。
 */
export class Node3D extends Node
{
    declare emitter: EventEmitter<Node3DEventMap>;
    declare __class__: "Node3D";

    declare protected _parent: Node3D;
    declare protected _children: Node3D[];

    /**
     * @private
     */
    _group: Group;

    get position()
    {
        return this._group.position;
    }
    set position(v)
    {
        this._group.position.set(v.x, v.y, v.z);
    }

    get rotation()
    {
        return this._group.rotation;
    }
    set rotation(v)
    {
        this._group.rotation.set(v.x, v.y, v.z, v.order);
    }

    get scale()
    {
        return this._group.scale;
    }
    set scale(v)
    {
        this._group.scale.set(v.x, v.y, v.z);
    }

    get quaternion()
    {
        return this._group.quaternion;
    }

    get up()
    {
        return this._group.up;
    }

    get visible()
    {
        return this._group.visible;
    }
    set visible(v)
    {
        this._group.visible = v;
    }

    /**
     * 是否拥有`Camera3D`组件。
     *
     * 为了解决`three.js`中摄像机、灯光独特`lookAt`运算。
     * @see https://github.com/mrdoob/three.js/blob/r149/src/core/Object3D.js#L284
     */
    get isCamera()
    {
        return !!this._group["isCamera"];
    }

    set isCamera(v)
    {
        this._group["isCamera"] = v;
    }

    get isPerspectiveCamera()
    {
        return !!this._group["isPerspectiveCamera"];
    }

    set isPerspectiveCamera(v)
    {
        this._group["isPerspectiveCamera"] = v;
    }

    get isOrthographicCamera()
    {
        return !!this._group["isOrthographicCamera"];
    }

    set isOrthographicCamera(v)
    {
        this._group["isOrthographicCamera"] = v;
    }

    /**
     * 是否拥有`Mesh3D`组件。
     */
    get isMesh()
    {
        return !!this._group["isMesh"];
    }

    set isMesh(v)
    {
        this._group["isMesh"] = v;
    }

    get isInstancedMesh()
    {
        return !!this._group["isInstancedMesh"];
    }

    set isInstancedMesh(v)
    {
        this._group["isInstancedMesh"] = v;
    }

    get isBrush()
    {
        return !!this._group["isBrush"];
    }

    set isBrush(v)
    {
        this._group["isBrush"] = v;
    }

    get isPoints()
    {
        return !!this._group["isPoints"];
    }

    set isPoints(v)
    {
        this._group["isPoints"] = v;
    }

    get isSprite()
    {
        return !!this._group["isSprite"];
    }

    set isSprite(v)
    {
        this._group["isSprite"] = v;
    }

    get isLine()
    {
        return !!this._group["isLine"];
    }

    set isLine(v)
    {
        this._group["isLine"] = v;
    }

    get isLineSegments()
    {
        return !!this._group["isLineSegments"];
    }

    set isLineSegments(v)
    {
        this._group["isLineSegments"] = v;
    }

    /**
     * 是否拥有`Light3D`组件。
     */
    get isLight()
    {
        return !!this._group["isLight"];
    }

    set isLight(v)
    {
        this._group["isLight"] = v;
    }

    get matrix()
    {
        return this._group.matrix;
    }
    set matrix(v)
    {
        this._group.matrix = v;
    }

    get matrixWorld()
    {
        return this._group.matrixWorld;
    }

    constructor()
    {
        super();
        this._group = new Group();

        this.emitter.on("added", this._onAdded, this);
        this.emitter.on("removed", this._onRemoved, this);
    }

    getObjectByName(name: string)
    {
        const object = this._group.getObjectByName(name);

        return object;
    }

    destroy()
    {
        super.destroy();

        this._group = null;
    }

    lookAt(x: number, y: number, z: number): void;
    lookAt(vector: Vector3): void;
    lookAt(...args: any)
    {
        if (typeof args[0] === "number")
        {
            this._group.lookAt(args[0], args[1], args[2]);
        }
        else
        {
            this._group.lookAt(args[0]);
        }
    }

    translateX(distance: number)
    {
        this._group.translateX(distance);

        return this;
    }

    translateY(distance: number)
    {
        this._group.translateY(distance);

        return this;
    }

    translateZ(distance: number)
    {
        this._group.translateZ(distance);

        return this;
    }

    updateMatrix()
    {
        this._group.updateMatrix();
    }

    updateMatrixWorld(force?: boolean)
    {
        this._group.updateMatrixWorld(force);
    }

    applyMatrix4(matrix: Matrix4)
    {
        this._group.applyMatrix4(matrix);
    }

    private _onAdded()
    {
        this.parent._group.add(this._group);
    }

    private _onRemoved()
    {
        this._group.parent.remove(this._group);
    }

    static get(group: Group)
    {
        let node3D = this._map.get(group);
        if (!node3D)
        {
            node3D = new Node3D();
            node3D._group = group;
            this._map.set(group, node3D);

            //
            group.children.forEach((child) =>
            {
                if (child.constructor === Mesh)
                {
                    const mesh3D: Mesh3D = Mesh3D.get(child);
                    node3D.addChild(mesh3D.entity);
                }
            });
        }

        return node3D;
    }
    private static _map = new Map<Group, Node3D>();
}
